﻿using System;
using System.Linq;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Send
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press input 1,2,3,4,5,enter.");

            var factory = new ConnectionFactory() { HostName = "192.168.6.88", Port = 5672, UserName = "test", Password = "123456" };
            //var factory_uri = new ConnectionFactory { Uri = new Uri("amqp://test:123456@192.168.6.88:5672/") };
            Demo6(factory);
            while (true)
            {
                var cmd = Console.ReadLine();
                switch (cmd)
                {
                    case "1":
                        Demo1(factory);
                        break;
                    case "2":
                        Console.WriteLine("Press input message.");
                        var message = Console.ReadLine();
                        Demo2(factory, message);
                        break;
                    case "3":
                        Demo3(factory);
                        break;
                    case "4":
                        Console.WriteLine("Press input info level.");
                        var level = Console.ReadLine();
                        Demo4(factory, level);
                        break;
                    case "5":
                        Console.WriteLine("Press input routingKey.");
                        var routingKey = Console.ReadLine();
                        Demo5(factory, routingKey);
                        break;
                    case "exit":
                        Environment.Exit(0);
                        break;
                    default:
                        break;
                }
            }
        }
        /// <summary>
        /// 基础demo
        /// </summary>
        /// <param name="factory"></param>
        static void Demo1(ConnectionFactory factory)
        {
            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    string queue = string.Format("MQ.{0}.Hello", "Demo1");
                    channel.QueueDeclare(queue: queue, durable: false, exclusive: false, autoDelete: false, arguments: null);
                    string message = "Hello World!";
                    var body = Encoding.UTF8.GetBytes(message);

                    channel.BasicPublish("", queue, null, body);
                    Console.WriteLine(" [1] Sent {0}", message);
                }
            }
        }
        /// <summary>
        /// 持久化
        /// </summary>
        /// <param name="factory"></param>
        /// <param name="message"></param>
        static void Demo2(ConnectionFactory factory, string message)
        {
            using (var connection = factory.CreateConnection())
            using (var channel = connection.CreateModel())
            {
                string queue = string.Format("MQ.{0}.Hello", "Demo2");
                channel.QueueDeclare(queue: queue,
                                     durable: true,
                                     exclusive: false,
                                     autoDelete: false,
                                     arguments: null);

                var body = Encoding.UTF8.GetBytes(message);

                var properties = channel.CreateBasicProperties();
                properties.Persistent = true;

                //持久化
                ////durable = true;
                //var properties = channel.CreateBasicProperties();
                //properties.DeliveryMode = 2;
                //channel.BasicPublish("", queue, properties, body);

                channel.BasicPublish(exchange: "",
                                     routingKey: queue,
                                     basicProperties: properties,
                                     body: body);
                Console.WriteLine(" [2] Sent {0}", message);
            }
        }
        /// <summary>
        /// Publish/Subscribe(fanout 模式)
        /// 一个消息可以到达多个bind的队列
        /// </summary>
        /// <param name="factory"></param>
        static void Demo3(ConnectionFactory factory)
        {
            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    string exchange = string.Format("Ex.{0}.Hello", "Demo3");
                    channel.ExchangeDeclare(exchange: exchange, type: ExchangeType.Fanout);
                    string message = "Hello World!";
                    var body = Encoding.UTF8.GetBytes(message);

                    channel.BasicPublish(exchange, "", null, body);
                    Console.WriteLine(" [3] Sent {0}", message);
                }
            }
        }
        /// <summary>
        /// Routing 模式
        /// </summary>
        /// <param name="factory"></param>
        /// <param name="level"></param>
        static void Demo4(ConnectionFactory factory, string level = "info")
        {
            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    string exchange = string.Format("Ex.{0}.Hello", "Demo4");
                    string routingKey = string.Format("{0}.log", level);
                    channel.ExchangeDeclare(exchange: exchange, type: ExchangeType.Direct);
                    string message = "[info] Hello World!";
                    var body = Encoding.UTF8.GetBytes(message);

                    channel.BasicPublish(exchange, routingKey, null, body);
                    Console.WriteLine(" [4] Sent {0}", message);
                }
            }
        }
        /// <summary>
        /// Topics 模式
        /// </summary>
        /// <param name="factory"></param>
        /// <param name="routingKey"></param>
        static void Demo5(ConnectionFactory factory, string routingKey)
        {
            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    string exchange = string.Format("Ex.{0}.Hello", "Demo5");
                    channel.ExchangeDeclare(exchange: exchange, type: ExchangeType.Topic);
                    string message = "[write] Hello World!";
                    var body = Encoding.UTF8.GetBytes(message);

                    channel.BasicPublish(exchange, routingKey, null, body);
                    Console.WriteLine(" [5] Sent {0}", message);
                }
            }
        }

        /// <summary>
        /// RPC
        /// </summary>
        /// <param name="factory"></param>
        static void Demo6(ConnectionFactory factory)
        {
            var connection = factory.CreateConnection();
            var channel = connection.CreateModel();
            string queue = string.Format("MQ.{0}.Hello", "Demo6");
            channel.QueueDeclare(queue: queue, durable: false, exclusive: false, autoDelete: false, arguments: null);
            channel.BasicQos(0, 1, false);
            var consumer = new EventingBasicConsumer(channel);
            channel.BasicConsume(queue: queue, autoAck: false, consumer: consumer);
            Console.WriteLine(" [6] Awaiting RPC requests");

            consumer.Received += (model, ea) =>
            {
                string response = null;

                var body = ea.Body.ToArray();
                var props = ea.BasicProperties;
                var replyProps = channel.CreateBasicProperties();
                replyProps.CorrelationId = props.CorrelationId;

                try
                {
                    Console.WriteLine(" [.] RoutingKey:{0} ", ea.RoutingKey);
                    var message = Encoding.UTF8.GetString(body.ToArray());
                    int n = int.Parse(message);
                    Console.WriteLine(" [.] fib({0})", message);
                    response = fib(n).ToString();
                }
                catch (Exception e)
                {
                    Console.WriteLine(" [.] " + e.Message);
                    response = "";
                }
                finally
                {
                    Console.WriteLine(" [.] fib={0} ", response);
                    var responseBytes = Encoding.UTF8.GetBytes(response);
                    channel.BasicPublish(exchange: "", routingKey: props.ReplyTo,
                        basicProperties: replyProps, body: responseBytes);
                    channel.BasicAck(deliveryTag: ea.DeliveryTag,
                        multiple: false);
                }
            };
        }
        private static int fib(int n)
        {
            if (n == 0 || n == 1)
            {
                return n;
            }

            return fib(n - 1) + fib(n - 2);
        }
    }
}
